﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.Xml;
using System.Xml.XPath;

class NetworkDungeon : Dungeon
{
    public SpawnPointList spawnPointList;
    int numPlayers = 0;
    public DungeonIndexer indexer;
    private bool coopMode = false;

    public List<Vector3> monsterSpawnPointList = new List<Vector3>();

    public NetworkDungeon(int size, int numPlayers, bool coopMode)
        :base(size, 10, 0.45f, 0.55f, 1, 0.2f, 0.8f)
    {
        this.numPlayers = numPlayers;
        this.coopMode = coopMode;
    }

    public NetworkDungeon(int size, byte[] serializedValue)
        : base(size, 10, 0.45f, 0.55f, 1, 0.2f, 0.8f)
    {
        this.Deserialize(serializedValue);
    }

    public override List<Room> GenerateRooms()
    {
        List<Room> rooms = base.GenerateRooms();

        // TODO: Task 1, spawn players in the same area
        // Add a "co-op" mode to the menu. When selected,
        // it will cause the spawn point list to create
        // spawn points in the same room.
        spawnPointList = new SpawnPointList(rooms, numPlayers, coopMode);

        return rooms;
    }

    public override List<Corridor> GenerateCorridors()
    {
        List<Corridor> corridors = base.GenerateCorridors();

        indexer = new DungeonIndexer(this);
        return corridors;
    }

    public void GenerateMonsters()
    {
        // Task 2, spawn monsters
        // Spawn fighting skeletons in each room based on the number of players. 
        int monstersPerRoom = (int)(numPlayers * 1.25f);

        // Spawn them only in rooms that the players don't spawn in.
        Room excludeRoom = spawnPointList.spawnRoom;


        foreach (Room spawnRoom in this.Rooms)
        {
            List<Vector3> roomSpawnPoints = new List<Vector3>();
            if (!(spawnRoom.X == excludeRoom.X && spawnRoom.Y == excludeRoom.Y))
            {
                int maxTries = monstersPerRoom;
                int currentTries = 0;
                bool placed = false;
                while (!placed && currentTries < maxTries)
                {
                    // We can spawn here
                    int randomXinRoom = UnityEngine.Random.Range(spawnRoom.X + 1, spawnRoom.X + spawnRoom.Width - 2);
                    int randomYinRoom = UnityEngine.Random.Range(spawnRoom.Y + 1, spawnRoom.Y + spawnRoom.Height - 2);

                    Vector2 spawnLocationCandidate = new Vector3(randomXinRoom, randomYinRoom, 0);
                    if (!roomSpawnPoints.Contains(spawnLocationCandidate))
                    {
                        roomSpawnPoints.Add(spawnLocationCandidate);
                        placed = true;
                    }
                    currentTries++;
                }
            }
            foreach (Vector3 newSpawnPoint in roomSpawnPoints)
                monsterSpawnPointList.Add(newSpawnPoint);
        }
    }

    public Vector3 GetNextSpawnPoint()
    {
        return spawnPointList.GetNextSpawnPoint();
    }

    public byte[] Serialize()
    {
        // Size of dungeon, tile data, number of spawn points, spawn point coordinates - heaven help us if the dungeon is bigger than 255 
        System.IO.MemoryStream dataStream = new System.IO.MemoryStream(1 + Size * Size + 1 + spawnPointList.spawnPoints.Count * 2);
        
        // Size of dungeon
        dataStream.WriteByte((byte)Size);   // heh, "byte size"

        // Tile data
        for (int y = 0; y < Size; y++)
        {
            for (int x = 0; x < Size; x++)
            {
                int tileDescriptor = indexer.GetTileIndex(x, y);
                if (tileDescriptor < 0)
                {
                    dataStream.WriteByte(255);
                }
                else
                {
                    dataStream.WriteByte((byte)tileDescriptor);
                }
            }
        }

        // Number of spawn points
        dataStream.WriteByte((byte)spawnPointList.spawnPoints.Count);

        // Spawn point coordinates
        foreach (Vector3 spawnPoint in spawnPointList.spawnPoints)
        {
            dataStream.WriteByte((byte)spawnPoint.x);
            dataStream.WriteByte((byte)spawnPoint.y);
        }

        return dataStream.ToArray();

    }

    private void Deserialize(byte[] serializedData)
    {
        indexer = DungeonIndexer.GetEmptyIndexer(this);
        System.IO.MemoryStream dataStream = new System.IO.MemoryStream(serializedData);

        int size = dataStream.ReadByte();
        for (int i = 0; i < size * size; i++)
        {
            int tileDescriptor = dataStream.ReadByte();
            if (tileDescriptor != 255)
            {
                // Add to the indexer
                indexer.TileDescriptors.Add(i, new TileDescriptor(tileDescriptor));
            }
        }

        spawnPointList = SpawnPointList.GetEmptyList();
        int spawnPointCount = dataStream.ReadByte();

        for (int i = 0; i < spawnPointCount; i++)
        {
            spawnPointList.spawnPoints.Add(new Vector3(dataStream.ReadByte(), dataStream.ReadByte(), 0));
        }

    }
}
